package littlecyberwar.util;

import littlecyberwar.util.*;
import littlecyberwar.util.*;

/**
 * This is a variant of the standard list type, except that
 * 1. It's circular: the last entry is connected to the first entry and is navigatable
 * 2. It's fixed: The size is fixed at creation time.
 * 3. It's designed to be fast.
 */
public class FixedCircularList {
  private int maxSize;
  private int currSize;
  private Object list[];
  private boolean cycleThrough = false;

  /**
   * The lru index
   */
  private int lru=0;
  private int mru=0;
  private int curr=0;

  /**
   * @parms sizeIn the size of the desired list
   * @parms cycleTHroughIn whether going back on the first item will cycle through
   * to the first item or not.
   */
  public FixedCircularList(int sizeIn, boolean cycleThroughIn) {
    maxSize = sizeIn;
    list = new Object[sizeIn];
    cycleThrough = cycleThroughIn;
    currSize=0;
  }

  /**
   * 1. advance the curr and mru to the next slot
   * 2. store the said object into the slot
   */
  public void add( Object objIn ) {
    if ( currSize == maxSize ) {
      // someone is being kicked out, the LRU need to adjust
      lru = advanceIndex ( lru );
    } else {
      currSize ++;
    }
    if ( curr == mru ) {
      curr = advanceIndex( curr );
      mru = advanceIndex( mru );
    } else {
      /**
       * Need to whack all the entries between curr and MRU
       */
      curr = advanceIndex( curr );
      clear();
    }
    list[curr] = objIn;
  }

  public Object getCurrent() {
    return ( list[curr] );
  }

  /**
   * 1. move the curr toward mru
   * 2. will return the curr item
   * Need to check if a potential wrap around case occures.
   * If wrap around is NOT permitted, then do not change.
   */
   public Object forward() {
    int val = advanceIndex ( curr );
    if ( cycleThrough || (val != lru) ) {
      // no cyclethrough allowed and we wrap around, need to back off
      curr = val;
    }
    return ( getCurrent() );
   }

  /**
   * 1. will return the curr item
   * 2. move curr toward back
   */
   public Object back() {
    Object obj = getCurrent();
    int val = retreatIndex( curr );
    if ( cycleThrough || (val != mru) ) {
      // no cyclethrough allowed and we wrap around, need to back off
      curr = val;
    }
    return ( obj );
   }

   /**
    * Will clear out all entries between curr & mru
    * reset mru to be current
    */
   private void clear() {
    while ( mru != curr ) {
      list[mru] = null;
      mru = retreatIndex ( mru );
    }
   }

   /**
    * Internal routine to advance the pointers and return new index
    */
    private int advanceIndex( int oldIndex ) {
      int val = oldIndex+1;
      if ( val >= maxSize ) {
        val = 0;
      }
      return ( val );
    }

   private int retreatIndex( int oldIndex ) {
    int val = oldIndex --;
    if ( val < 0 ) {
      val = maxSize-1;
    }
    return ( val );
   }

   public void dump() {
    Debug.out(this.toString());
    Debug.out("** Physical Storage **");
    for ( int i=0; i < maxSize; i++ ) {
      Debug.out("--- entry[" + i + "]= "+ list[i].toString());
    }
    Debug.out("LRU= " + lru);
    Debug.out("MRU= " + mru);
    Debug.out("CURR= " + curr);
   }
}